#include "current_time.h"

#include <linux/proc_fs.h>
#include <linux/seq_file.h>

#include "log.h"

#define NS_PER_SEC 1000000000

struct jit_current_time_context {
    bool active;
};

static void* jit_current_time_start(struct seq_file *seq, loff_t *pos) {
    LOGI("jitcurrenttime start pos: %lld", *pos);
    struct jit_current_time_context *ctx = seq->private;
    if (!ctx) {
        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
        if (!ctx) {
            LOGE("out of memory");
            return NULL;
        }
        LOGI("alloc new context 0x%p", ctx);
        ctx->active = true;
        seq->private = ctx;
    }
    LOGI("jitcurrenttime start exit");
    return ctx;
}

static void* jit_current_time_next(struct seq_file *seq, void *v, loff_t *pos) {
    LOGI("jitcurrenttime next pos: %lld", *pos);
    struct jit_current_time_context *ctx = seq->private;

    if (signal_pending(current) || !ctx->active) {
        LOGI("interrupted");
        ctx->active = false;
        return NULL;
    }

    (void)(*pos)++;
    LOGI("jitcurrenttime next exit pos: %lld", *pos);
    return ctx;
}

static int jit_current_time_show(struct seq_file *seq, void *v) {
    LOGI("jitcurrenttime show");
    u64 ns = ktime_get_ns();            // uptime in ns
    u64 real_ns = ktime_get_real_ns();  // walltime in ns
    struct timespec64 ts, real_ts;
    ktime_get_ts64(&ts);                // uptime in seconds and ns
    ktime_get_real_ts64(&real_ts);      // walltime in seconds and ns
    seq_printf(seq, "0x%08lx 0x%016llx %12u.%u\n", jiffies, jiffies_64, (u32)(ns/NS_PER_SEC), (u32)(ns%NS_PER_SEC));
    seq_printf(seq, "%11s %18s %12llu.%lu\n", "", "", ts.tv_sec, ts.tv_nsec);
    seq_printf(seq, "%11s %18s %12u.%u\n", "", "", (u32)(real_ns/NS_PER_SEC), (u32)(real_ns%NS_PER_SEC));
    seq_printf(seq, "%11s %18s %12llu.%lu\n", "", "", real_ts.tv_sec, real_ts.tv_nsec);
    return 0;
}

static void jit_current_time_stop(struct seq_file *seq, void *v) {
    LOGI("jitcurrenttime stop");
    struct jit_current_time_context *ctx = seq->private;

    if (ctx) {
        LOGI("free context 0x%p", ctx);
        kfree(ctx);
        seq->private = NULL;
    }
    LOGI("jitcurrenttime stop exit");
}

static const struct seq_operations jit_current_time_ops = {
    .start = jit_current_time_start,
    .next = jit_current_time_next,
    .show = jit_current_time_show,
    .stop = jit_current_time_stop,
};

static int jit_current_time_open(struct inode *inode, struct file *filp) {
    return seq_open(filp, &jit_current_time_ops);
}

// For the situation having only a single record,
// we can use single_open/release impl.
const struct proc_ops current_time_ops = {
    .proc_open = jit_current_time_open,
    .proc_read = seq_read,
    .proc_lseek = seq_lseek,
    .proc_release = seq_release,
};
